Сервер.ПротоколОбмена
Сервер расположен по адресу http://95.31.213.51:8080/server/rest ВЕСЬ ОБМЕН XML ПРОИСХОДИТ В КОДИРОВКЕ UTF-8 Формат дат http://www.w3.org/TR/xmlschema-2/#dateTime Использовать формат без временной зоны, т.е. UTC Пример: 2011-09-01T11:40:45.629Z Если миллисекунды равны нулю, то сервер может их не присылать: 2011-09-01T11:40:45Z Регистрация Путь: /register Тип запроса: POST XML: password Если регистрация завершена успешно, то возвращается обЪект user с дополнительными заполненными полями, такими, как id, version, subscriptionType. Авторизация не требуется. Авторизация Путь: /version/{clientType} Тип запроса: GET Возвращаемый XML: v1 v2 v1 Путь: /auth тип запроса: GET Возвращаемый XML: LOCAL SYNC MULTI Процесс подключения к серверу и авторизации должен выглядеть следующим: 1. Первый запрос на сервер ОБЯЗАН приходить к урл /version/clientType, где clientType - это строка, идентифицирующая тип и версию клиента: win1_0, android1_2_3 и т.п. Строка должна содержать только англ. буквы, цифры и подчеркивание. Обратно получается список серверов, с которыми должна идти дальнейшая работа. У каждого сервера указан список feature - это версии протоколов, поддерживаемые сервером. В настоящее время (до первого релиза) существует только версия 'v1', обозначающая текущую реализацию. Клиент обязан знать версии, которые он поддерживает, соответственно выбирать нужные сервера и уведомлять пользователя, если текущая версия клиента не поддерживается сервером (требуется обновление) Если серверов несколько, то рабочий должен выбираться рандомом - это важно для равномерного распределения нагрузки. 2. Зная IP, можно регистрировать пользователей (см. соответствующий раздел), либо проверить логин и пароль пользователя. Для этого следует сделать GET-запрос на /auth. Возвращенный XML содержит список доступного функционала для этого пользователя. Возможные варианты: * LOCAL без синхронизации * SYNC синхронизация * MULTI работа с поручениями. Список может содержать несколько элементов, например, сейчас тарифный план MULTI включаeт в себя всё: LOCAL, SYNC, MULTI. Поле user.subscriptionType - это название тарифного плана, видимое пользователю, к нему привязывать клиенты нельзя. 3. Проверив логин с паролем и получив список разрешений для пользователя, можно делать синхронизацию Если у пользователя проблемы с тарифным планом, то сервер будет возвращать HTTP 403 или один из кодов, специфичных для проверки тарифных планов. Обновление данных Путь: /update/{version} Тип запроса: GET XML: В поле {version} следует указывать версию, пришедшую в updateVersion с предыдущем апдейтом. Для первого апдейта в качестве версии нужно указывать 0: /update/0 Для доступа необходимо авторизоваться с логином и паролем, указанным при регистрации Заливка данных Путь: /commit Тип запроса: POST XML запроса: XML ответа: При коммите пользователь должен отправить все измененные данные с заполненым полем clientId. В ответе он получает информацию о каждом отправленном обЪекте - присвоенный сервером ID и версия обЪекта. Эти поля должны быть сохранены в базе. Связать отправленные и полученные данные можно по clientId. ЗАМЕТКА: clientId должен быть уникальным, т.е. если отправляются данные из разных таблиц, то имеет смысл перед отправкой добавлять к нему тип: Project id="17" уходит как clientId="project_17" Биндинг для передаваемых сущностей Ошибки Если выполнение команды завершилось ошибкой, то сервер пришлет информацию в следующем формате: " error: need to narrow /dev/hands narrow /dev/hands Известные коды ошибок: UNEXPECTED("Unexpected server error: %s"), //что-то случилось на сервере TEST("Test error!"), TEST_WITH_ARGS("Test error: %s!"), INVALID_VERSION("Invalid version: %s"), INVALID_ID("Invalid id: %s"), PROJECT_SAVE_AUTHOR("Only author can modify project %s"), //код ошибки, возвращаемый при конфликтах коммита. //первый аргумент - это тип сущности - PROJECT, USER, TASK //второй - это серверный id сущности //эта ошибка появляется если: //в базе нет сущности с таким id и версией //в базе просто нет такой сущности SYNCH_UPDATE_REQUIRED("Update is required for %s, id=%s"), //возвращается при попытке обновления сущности, отмеченной как удаленная SYNCH_ALREADY_DELETED("%s is already deleted - update denied, id=%s"), //неизвестный код статуса проекта при приеме от клиента PROJECT_INVALID_STATUS_CODE("Unknown status code %s for project %s"), //возвращается при регистрации пользователя, когда одно из полей не заполнено (поле пересылается в том же формате, в котором фигурирует в User.java) REGISTER_MISSING_REQUIRED_FIELD("Field %s is required"), REGISTER_WRONG_LOGIN("Login should be valid e-mail address"), REGISTER_DUPLICATE_LOGIN("Login %s is already used"), //Возвращается при попытке послать на сервер объект со слишком длинной строкой в одном из полей FIELD_TOO_LONG("Field size can't be more than %s characters. Data: \"%s\""), //Возвращается, если неизвестный код статуса задачи или статус не указан TASK_INVALID_STATUS_CODE("Unknown status code %s for task %s"), //Возвращается, если неизвестный код типа запланированности задачи или тип не указан TASK_INVALID_PLANNEDTYPE_CODE("Unknown planned type code %s for task %s"), //При попытке отправить запрос на авторизацию от имени другого пользователя или множественный запрос USER_BAD_AUTH_REQUEST("User authorization request should contain only user's login: %s"), //при попытке добавить авторизованного пользователя без запроса авторизации. USER_UNAUTHORIZED_ADD("Unable to add user %s without authorization request"), //При попытке изменить список запросов на авторизацию как-либо помимо удаления запроса. USER_BAD_AUTH_MODIFY("Unable to modify auth requests - only deletion allowed, user: %s"), USER_SYNC_CREATE_NOT_ALLOWED("Unable to create user %s during synchronization - use registration instead"), USER_COMMIT_ANOTHER_USER("Commiting another user is not allowed"), //Попытка назначить поручение человеку, не являющимся авторизованным в контакт листе TASK_WORKER_AUTHORIZED_ONLY("Unable to assign a task to unauthorized user %s"), //попытка синхронизации задачи, к которой у пользователя нет доступа(не является автором или исполнителем) TASK_ACCESS_DENIED("User %s does not have access to task '%s'"), //попытка синхронизации элемента переписки с неверным кодом типа TASK_INVALID_POST_TYPE_CODE("Invalid task post type code: '%s'"), //попытка отправки измененного элемента переписки на синхронизацию TASK_POSTITEM_UPDATE_NOT_ALLOWED("post item update not allowed"), //отсутствие одного из полей при коммите задачи с аттачментом TASK_INVALID_ATTACHMENT("invalid attachment: %s"), USER_PASSWORD_TOO_SHORT("User password should be at least %s chars long"), //неверная попытка изменить статус поручения исполнителем TASK_INVALID_STATUS_CHANGE("Unable to set status %s of task %s"), FILE_IS_TOO_LARGE("Upload file size cannot exceed %s megabytes"), //неудачная попытка добавить подписку к аккаунту. SUBSCRIBE_FAILED("Adding subscription failed: %s"), //попытка использовать функционал, не входящий в текущую подписку WRONG_SUBSCRIPTION("This operation is not allowed by your subscription type: %s"), //запрос восстановления пароля для несуществующего логина PASSRESET_USER_NOT_FOUND("Username not found: %s"), PASSRESET_INVALID_CODE("Invalid password reset code"), //при превышении квоты на суммарный объем залитых данных USED_SPACE_QUOTA("Upload failed - you are out of size quota (%s GB)"); Клиентские параметры Предназначены для хранения конфигурации, специфичной для юзера. Хранятся внутри объекта user как множество элементов следующего вида: Названия параметров не могут содержать пробелы и точки. Параметры, распознаваемые сервером Параметры, распознаваемые всеми клиентами Добавить по необходимости, с префиксом client. - автоматически переносить в архив - автоматически синхронизировать - сортировка сообщений в переписке по дате (снизу вверх или сверху вниз) Прочие параметры Добавить по необходимости, с каким-нибудь префиксом Заливка файлов Используется для заливки на сервер аватарок и прикрепленных файлов. Заливка файла Путь: /file/name/{fileName} , где fileName - имя файла, который заливается Тип запроса: POST Требуется авторизация. После успешной заливки будет возвращен XML следующего вида: По этому id можно в дальнейшем загрузить файл Существует ограничение на максимальный размер заливаемого файла - на данный момент это 20 Мб. Загрузка файла Путь: /file/{id} Тип запроса: GET Требуется авторизация Заливка аватара Этот сервис не является обязательным, т.к. залить аватар можно следующим способом: * Заливаем файл с аватаром на сервер * Апдейтим своего пользователя * Вписываем ему в поле avatar идентификатор залитого файла * Коммитим своего пользователя Для упрощения процедуры сделан дополнительный сервис: Путь: /avatar Типа запроса: POST Требуется авторизация. Сервис автоматически прикрепляет залитый файл в качестве аватарки для текущего пользователя. Клиенту нужно будет только сделать потом апдейт. Возвращаемые значения - как в сервисе заливки файлов Учет прочитанных поручений Этот функционал позволяет клиенту определить, читал ли текущий пользователь указанное поручение на другом клиенте - для вкладки "Измененные". 1. Уведомление сервера о прочтении поручения либо элемента области переписки: Путь: /read Метод: POST Xml: В XML указывается id и версия поручения или элемента переписки, которые пользователь прочитал 2. Уведомление клиента о том, прочитано поручение или нет см. поле Task.read и PostItem.read - оно будет равно true при апдейте, если другой клиент предварительно отослал на сервер уведомление о прочтении. Если клиент прочитал сообщение и послал /read, то при очередном апдейте прийдет ReadNotification с objectId/objectVersion того, что было прочитано. В этом случае клиент должен найти у себя в базе объект с таким же id и версией и отметить его как прочитанный. Если не найден, то проигнорировать ReadNotification. Сохранять полученные ReadNotification в базе смысла нет. Помимо использования этого механизма, клиенты должны сами хранить признак "прочитанности" поручения/элемента переписки локально. Нужно учитывать, что в момент открытия поручения интернета может и не быть, так что прочитанные поручения и элементы переписки нужно запоминать и отсылать уведомления пачкой при очередной синхронизации(либо при появлении инета). Общий алгоритм выглядит примерно так: при открытии поручения на прочтение делается коммит /read для поручения и всех элементов переписки, которые еще не отмечены локально как read. После чего локальное поручение и элементы переписки отмечаются как read. При апдейте если у поручения и всей переписки выставлен признак read, значит эту задачу этот пользователь уже читал. Для задач этот механизм не имеет смысла - если юзер закоммитил задачу, значит он её уже однозначно читал. Коды статуса проекта /** Выполняется*/ WORK, /** Выполнено*/ DONE, /** Архив*/ ARCHIVE, /** Отменен*/ CANCELLED Коды статуса задачи //выполняется WORK, //в работе - исполнитель прочитал поручение и выполняет его WORK_IN_PROGRESS, //выполнено DONE, //архив ARCHIVE, //отменено CANCELLED, //корректировка CORRECTION Переписка в поручениях Коды типа элемента переписки /** Комментарий*/ COMMENT, /** Отчет*/ REPORT, /** Запрос корректировки*/ CORRECT, /** Прикрепленный файл*/ FILE Удаление В каждом обЪекте есть свойство deleted - если пользователь что-то удаляет, то клиент обязан выставить это свойство в true и закоммитить обЪект. Если после апдейта свойство deleted = true, значит обЪект был удален на другом устройстве и удаление было закоммичено. Типы запланированности задачи /** Запланировано - дата планирования содержит валидную дату*/ PLANNED, /** Запланировано на вкладку "Следующие" */ NEXT, /** Запланировано на вкладку "Когда-нибудь" */ SOMEDAY, /** Не запланировано*/ UNPLANNED Авторизация и добавление пользователей в контакт-лист Авторизация реализована примерно как в скайп - один из пользователей отправляет запрос на авторизацию, второй подтверждает и они автоматом добавляют друг друга в контакты. * user1 добавляет к себе в контакт нового пользователя с authorized=false * user1 коммитит, после чего у user2 в authRequests появится логин user1 * user2 апдейтится и либо добавляет к себе в контакт user1 authorized=true, либо удаляет из authRequests user1 * после коммита и апдейта оба юзера будут иметь в контактах друг друга ВНИМАНИЕ: Т.к. вашего юзера могут модифицировать извне (при авторизации), то при коммите будут конфликты - нужно аккуратно мерджить входящие изменения (список контактов и authRequests) c исходящими(список контактов, фио, пароль и т.п.) Для лучшего понимания привожу названия тестовых случаев: *При сохранении другого пользователя возвращается ошибка *При запросе на авторизацию к список контактов authorized=false второму юзеру отсылается логин в authRequests *При отзыве запроса на авторизацию у второго юзера убирается это имя из authRequest *При отказе в авторизации у первого юзера удаляется контакт *При подтверждении авторизации обоим юзерам добавляется контакт authorized=true *При удалении юзера из контакта этот юзер удаляется из контакта другого юзера *ВСТРЕЧНАЯ АВТОРИЗАЦИЯ. Если с при апдейте u2 пришел u1 ,а у u2 уже есть запрос на авторизацию u1 , то не коммитим на сервер этот запрос. *Нельзя добавлять в список контактов authorized=true, если нет запроса на авторизацию от этого юзера *При сохранении юзера запросы авторизации могут только удаляться, а не добавляться или меняться Поиск пользователей Путь: /searchUsers Тип запроса: POST XML запроса: Ответ: со списком пользователей, удовлетворяющих запросу Логин и почта ищется по полному совпадению, имя и фамилия - по подстроке без учета кейса. Google C2DM (Android push) Этот функционал позволяет отправлять уведомления об обновленных данных на устройства под управлением Android. Существует всего один тип уведомления и он означает, что следует выполнить синхронизацию. В настоящее время гарантируется, что если данные пользователя были изменены, то устройство получит уведомление, но не гарантируется обратное - т.е. возможно получение "ложных" уведомлений, синхронизация после которых ничего не даст. Активация сервиса: при авторизации на /auth следует передать registrationId приложения через GET-параметр "c2dmId". Пример: /auth?c2dmId=123456qwerty После этого приложение будет получать уведомления следующего вида: {"data.user": "useremail@host.net", "data.uid":"12345"} где uid - это идентификатор рассылки. он уникален для каждого события, но один и тот же для всех устройств, которым это событие было послано. Назначение - фильтрация дубликатов, если устройство подписано на событие два раза. Отзывы пользователей Путь: /opinion Тип запроса: POST XML запроса: opinion text mark - оценка пользователя (в звездах, попугаях, галочках и т.п.) 0 обозначает нейтральную оценку, положительное число - положительную, отрицательное - отрицательную. Предлагаю шкалу -2 -1 0 1 2 (оч. плохо - плохо - сойдет - хорошо - очень хорошо) text - текст отзыва. Как и у всех "длинных" полей, его длина не может превышать 64к символов. Ответ: HTTP 200 и пустой ответ в случае удачной отправки. Сброс пароля 1. Запрос на сброс пароля Тип запроса: GET путь: /requestPasswordReset?email={email} Возвращаемое значение: HTTP 200 и пустой ответ в случае успеха. После вызова пользователю уйдет на почту письмо со ссылкой на офсайт. 2. Сброс пароля Тип запроса: GET путь: /resetPassword?email={email}&code={code}&password={password} Возвращаемое значение: HTTP 200 и пустой ответ в случае успеха. code - это код сброса пароля, который был в письме